package cate.game.play.base;


import cate.common.table.d.GDPos;
import cate.common.table.d.GDSkill;
import cate.common.util.XT;
import cate.game.play.fighter.Fighter;
import cate.game.play.part.TeamVS;
import cate.game.play.proce.move.MoveCtx;
import cate.game.play.skill.Skill;
import easy.java.dev.note.NoteClass;

import java.util.*;
import java.util.stream.Collectors;

import static cate.common.table.d.GDPos.*;

@NoteClass("技能目标查找")
public class TargetSelector {



    /** 位置优先级增加的权重 */
    private static final int POS_WEIGHT = 1;

    /** 属性优先级增加的权重 */
    private static final int ATTR_WEIGHT = 10;

    /** 职业优先级增加的权重 */
    private static final int JOB_WEIGHT = 100;

    /** 阵营优先级增加的权重 */
    private static final int CAMP_WEIGHT = 100;

    /** 标签优先级增加的权重 */
    private static final int TAG_WEIGHT = 100;

    /** 最高优先级的权重 */
    private static final int TOP_WEIGHT = 1000;



    /** 目标代理 */
    public static class TargetProxy {
        /** 战斗者 */
        public Fighter fighter;
        /** 权重 */
        public int weight = 0;
        /** 可被选 */
        public boolean enabled = true;

        public TargetProxy(Fighter fighter) {
            this.fighter = fighter;
        }
    }


    public static List<Fighter> doFind(Fighter actor, TeamVS teamVS, TargetFilterParam param) {
        List<Fighter> scopes = findByScope(teamVS, actor, param.getScope(),param.isAlive());
        List<TargetProxy> targetProxies = scopes.stream().map(TargetProxy::new).collect(Collectors.toList());
        Collections.shuffle(targetProxies);
        handleTop(targetProxies,param.getTopPriorityPids());
        handleJob(targetProxies, param.getCJobs());
        handleCamp(targetProxies, param.getCCamps());
        handleAttr(targetProxies, param.getCAttr());
        handleSkillTags(targetProxies, param.getCSkillTags());
        handleBuffTags(targetProxies, param.getCBuffTags());
        handleCSpecial(targetProxies, param.getSpecial(), actor.getPos());
        targetProxies.removeIf(a -> !a.enabled);
        if (targetProxies.size() > param.getTargetNum()) {
            targetProxies.sort(Comparator.comparing(a -> -a.weight));
            targetProxies = new ArrayList<>(targetProxies.subList(0, param.getTargetNum()));
        }
        return targetProxies.stream().map(a -> a.fighter).collect(Collectors.toList());
    }

    public static Fighter findOne(Fighter actor, TeamVS teamVS, TargetFilterParam param) {
        List<Fighter> targets = doFind(actor, teamVS, param);
        if (XT.isNotBlank(targets)) {
            return targets.get(0);
        }
        return null;
    }


    /**
     * 查找技能目标
     * @param move
     * @param skill
     * @return
     */
    public static List<Fighter> findBySkill(MoveCtx move, Skill skill) {
        return doFind(move.getActor(), move.getTeamVS(), TargetFilterParam.buildBySkill(skill));
    }

    public static List<Fighter> findByScope(TeamVS teamVS,Fighter actor, byte tarScope) {
        return findByScope(teamVS,actor,tarScope,true);
    }

    public static List<Fighter> findByScope(TeamVS teamVS, Fighter actor, byte tarScope, boolean isAlive) {
        List<Fighter> scopes = new ArrayList<>();
        switch (tarScope) {
            case GDSkill.Scope.ENEMY:
                scopes.addAll(teamVS.getEnemyTeam(actor.getTeam()).getAreaFighters());
                break;
            case GDSkill.Scope.WE:
                scopes.addAll(actor.getFightTeam().getAreaFighters());
                break;
            case GDSkill.Scope.FRIENDS:
                scopes.addAll(actor.getFightTeam().getFriends(actor));
                break;
            case GDSkill.Scope.SELF:
                scopes.add(actor);
                break;
            case GDSkill.Scope.ALL:
                scopes.addAll(teamVS.getAreaFighters());
                break;
            default:
                break;
        }
        scopes.removeIf(f -> f.isAlive() != isAlive);
        return scopes;
    }


    /**
     * 优先选择目标的处理
     * @param targetProxies
     * @param tarPids
     */
    private static void handleTop(List<TargetProxy> targetProxies, List<Integer> tarPids) {
        if (XT.isBlank(tarPids)) {
            return;
        }
        for (TargetProxy candidate : targetProxies) {
            if (tarPids.contains(candidate.fighter.getPid())) {
                candidate.weight += TOP_WEIGHT;
            }
        }
    }

    /**
     * 职业排序
     * @param candidates
     */
    private static void handleJob(List<TargetProxy> candidates, List<Byte> jobs) {
        if(XT.isBlank(jobs)){
            return;
        }
        for (TargetProxy candidate : candidates) {
            boolean containJob = jobs.contains(candidate.fighter.getJob());
            if (containJob) {
                candidate.weight += JOB_WEIGHT;
            }
        }
    }

    /**
     * 阵营排序
     * @param candidates
     */
    private static void handleCamp(List<TargetProxy> candidates, List<Byte> camps) {
        if (XT.isBlank(camps)) {
            return;
        }
        for (TargetProxy candidate : candidates) {
            boolean containCamp = camps.contains(candidate.fighter.getCamp());
            if (containCamp) {
                candidate.weight += CAMP_WEIGHT;
            }
        }
    }

    /**
     * 属性排序
     * @param targetProxies
     */
    private static void handleAttr(List<TargetProxy> targetProxies, Integer attr) {
        if (attr == null) {
            return;
        }
        //需要选择属性大的
        boolean needBig = attr > 0;
        int absAttr = Math.abs(attr);

        targetProxies = targetProxies.stream().sorted(Comparator.comparing(t -> t.fighter.attr.getTotalValue(absAttr))).collect(Collectors.toList());
        for (int i = 0; i < targetProxies.size(); i++) {
            TargetProxy targetProxy = targetProxies.get(i);
            int weight = i * ATTR_WEIGHT;
            if (needBig) {
                targetProxy.weight += weight;
            } else {
                targetProxy.weight -= weight;
            }
        }
    }

    /**
     * 技能标签排序
     * @param candidates
     */
    private static void handleSkillTags(List<TargetProxy> candidates, List<String> skillTags) {
        if(XT.isBlank(skillTags)){
            return;
        }
        for (TargetProxy candidate : candidates) {
            boolean containTag = candidate.fighter.skill.containTag(skillTags);
            if (containTag) {
                candidate.weight += TAG_WEIGHT;
            }
        }
    }

    private static void handleBuffTags(List<TargetProxy> candidates, List<String> buffTags) {
        if(XT.isBlank(buffTags)){
            return;
        }
        for (TargetProxy candidate : candidates) {
            boolean containTag = candidate.fighter.buff.containTags(buffTags);
            if (containTag) {
                candidate.weight += TAG_WEIGHT;
            }
        }
    }

    private static void handleCSpecial(List<TargetProxy> candidates,Byte cSpecial,byte actorPos){
        if (cSpecial == null) {
            return;
        }
        switch (cSpecial) {
            case GDSkill.CSpecial.ATTACK:
                handleAttack(candidates, actorPos);
                break;
            case GDSkill.CSpecial.SINGLE_FRONT:
                handleSingleFrontRow(candidates, actorPos);
                break;
            case GDSkill.CSpecial.FRONT_ONE_ROW:
                handleFrontOneRow(candidates);
                break;
            case GDSkill.CSpecial.BACK_ONE_ROW:
                handleBackOneRow(candidates);
                break;
            case GDSkill.CSpecial.FRONT_ONE_COL:
                handleFrontOneCol(candidates, actorPos);
                break;
            case GDSkill.CSpecial.SINGLE_FRONT_ROW:
                handleSingleFrontRow(candidates);
                break;
            case GDSkill.CSpecial.SINGLE_BACK_ROW:
                handleSingleBackRow(candidates);
                break;
            case GDSkill.CSpecial.FRONT_EXIST_COL:
                handleFrontExistCol(candidates,actorPos);
                break;
            default:
                break;
        }
    }



    /** 普通攻击 */
    private static void handleAttack(List<TargetProxy> candidates, byte actorPos) {
        byte[] choosePoses;
        switch (actorPos){
            case P1:
            case P4:
                choosePoses = new byte[]{P1, P2, P3, P4, P5, P6};break;
            case P2:
            case P5:
                choosePoses = new byte[]{P2, P1, P3, P5, P4, P6};break;
            case P3:
            case P6:
                choosePoses = new byte[]{P3, P1, P2, P6, P4, P5};break;
            default:
                choosePoses = new byte[]{};
        }
        Map<Byte, Integer> posWeight = new HashMap<>();
        for (int i = 0; i < choosePoses.length; i++) {
            posWeight.put(choosePoses[i], POS_WEIGHT * (choosePoses.length - i));
        }
        posWeightSet(candidates, posWeight);
    }


    /** 前方单体 */
    private static void handleSingleFrontRow(List<TargetProxy> candidates,  byte actorPos) {
        byte[] choosePoses;
        switch (actorPos){
            case P1:
            case P4:
                choosePoses = new byte[]{P1, P4, P2, P5, P3, P6};break;
            case P2:
            case P5:
                choosePoses = new byte[]{P2, P5, P1, P4, P3, P6};break;
            case P3:
            case P6:
                choosePoses = new byte[]{P3, P6, P1, P4, P2, P5};break;
            default:
                choosePoses = new byte[]{};break;
        }
        Map<Byte, Integer> posWeight = new HashMap<>();
        for (int i = 0; i < choosePoses.length; i++) {
            posWeight.put(choosePoses[i], POS_WEIGHT * (choosePoses.length - i));
        }
        posWeightSet(candidates, posWeight);
    }


    /**
     * 前一排
     * @param candidates
     */
    private static void handleFrontOneRow(List<TargetProxy> candidates){
        handleExist(candidates, R1_ARRAY, R2_ARRAY);
    }


    /**
     * 后一排
     * @param candidates
     */
    private static void handleBackOneRow(List<TargetProxy> candidates){
        handleExist(candidates, R2_ARRAY, R1_ARRAY);
    }


    /** 前方一列 */
    private static void handleFrontOneCol(List<TargetProxy> candidates,  byte actorPos) {
        switch (actorPos) {
            case P1:
            case P4:
                handleExist(candidates, C2_ARRAY, C1_ARRAY, C3_ARRAY);break;
            case P2:
            case P5:
                handleExist(candidates, C1_ARRAY, C2_ARRAY, C3_ARRAY);break;
            case P3:
            case P6:
                handleExist(candidates, C3_ARRAY, C2_ARRAY, C1_ARRAY);break;
            default:break;
        }
    }

    /** 检测指定位置是否有人 */
    private static void handleExist(List<TargetProxy> candidates, byte[]...posArrays) {
        for (byte[] posArray : posArrays) {
            if (numInPos(candidates, posArray) > 0) {
                selectByPos(candidates, posArray);
                return;
            }
        }
    }

    /** 检测前排有人的一列 */
    private static void handleFrontExistCol(List<TargetProxy> candidates, byte[]... posArrays) {
        byte[] targetArray = new byte[]{};
        //优先找前排
        for (byte[] array : posArrays) {
            if (existInPos(candidates, array[0])) {
                targetArray = array;
                break;
            }
        }
        //没找到就找后排
        if (targetArray.length == 0) {
            for (byte[] array : posArrays) {
                if (existInPos(candidates, array[1])) {
                    targetArray = array;
                    break;
                }
            }
        }
        selectByPos(candidates, targetArray);
    }

    /** 只能选择指定位置的人 */
    private static void selectByPos(List<TargetProxy> candidates, byte[] posArr) {
        for (TargetProxy tp : candidates) {
            tp.enabled = false;
            for (byte pos : posArr) {
                if (pos == tp.fighter.getPos()) {
                    tp.enabled = true;
                    break;
                }
            }
        }
    }

    /** 有没有在指定位置的目标 */
    private static int numInPos(List<TargetProxy> candidates, byte[] posArr) {
        int num = 0;
        for (TargetProxy tp : candidates) {
            for (byte pos : posArr) {
                if (tp.enabled && pos == tp.fighter.getPos()) {
                    num++;
                    break;
                }
            }
        }
        return num;
    }

    /** 指定位置有人 */
    private static boolean existInPos(List<TargetProxy> candidates, byte pos) {
        for (TargetProxy tp : candidates) {
            if (tp.enabled && pos == tp.fighter.getPos()) {
                return true;
            }
        }
        return false;
    }




        /** 根据位置权重map设置权重 */
    private static void posWeightSet(List<TargetProxy> candidates, Map<Byte, Integer> posWeight) {
        for (TargetProxy tp : candidates) {
            Integer weight = posWeight.get(tp.fighter.getPos());
            tp.weight += weight != null ? weight : 0;
        }
    }


    /** 前排单体 */
    private static void handleSingleFrontRow(List<TargetProxy> candidates) {
        byte[] choosePoses = new byte[]{P1, P2, P3, P4, P5, P6};
        Map<Byte, Integer> posWeight = new HashMap<>();
        for (int i = 0; i < choosePoses.length; i++) {
            posWeight.put(choosePoses[i], POS_WEIGHT * (choosePoses.length - i));
        }
        posWeightSet(candidates, posWeight);
    }


    /** 后排单体 */
    private static void handleSingleBackRow(List<TargetProxy> candidates) {
        byte[] choosePoses = new byte[]{ P4, P5, P6, P1, P2, P3};
        Map<Byte, Integer> posWeight = new HashMap<>();
        for (int i = 0; i < choosePoses.length; i++) {
            posWeight.put(choosePoses[i], POS_WEIGHT * (choosePoses.length - i));
        }
        posWeightSet(candidates, posWeight);
    }

    /** 前方有人的一列 */
    private static void handleFrontExistCol(List<TargetProxy> candidates,byte actorPos) {
        switch (actorPos) {
            case P1:
            case P4:
                handleFrontExistCol(candidates, C2_ARRAY, C1_ARRAY, C3_ARRAY);break;
            case P2:
            case P5:
                handleFrontExistCol(candidates, C1_ARRAY, C2_ARRAY, C3_ARRAY);break;
            case P3:
            case P6:
                handleFrontExistCol(candidates, C3_ARRAY, C2_ARRAY, C1_ARRAY);break;
            default:break;
        }
    }



    /**
     * 根据继承的目标重选相对位置的单位
     * @param retarget
     * @return
     */
    public static List<Fighter> findRetargetTargets(TeamVS teamVS,byte retarget, List<Fighter> extendTargets) {
        if (XT.isBlank(extendTargets)) {
            return new ArrayList<>();
        }
        List<Fighter> alives = teamVS.getTeam(extendTargets.get(0).team).getFightersAlive();
        if (XT.isBlank(alives)) {
            return new ArrayList<>();
        }
        switch (retarget) {
            case GDSkill.Retarget.NEAR:
                return findNear(alives, extendTargets);
            case GDSkill.Retarget.SAME_ROW:
                return findSameRow(alives, extendTargets);
            case GDSkill.Retarget.SAME_COL:
                return findSameCol(alives, extendTargets);
            case GDSkill.Retarget.OTHER:
                return findOther(alives, extendTargets);
            default:
                return new ArrayList<>();
        }
    }

    /**
     * 查找溅射范围的fighter
     * @param aliveFighters
     * @param parentTargets
     * @return
     */
    public static List<Fighter> findNear(List<Fighter> aliveFighters, List<Fighter> parentTargets) {
        Set<Fighter> targets = new HashSet<>();
        for (Fighter parentTarget : parentTargets) {
            byte[] nearPos = findNearPos(parentTarget.getPos());
            for (Fighter fighter : aliveFighters) {
                for (byte pos : nearPos) {
                    if (pos == fighter.getPos()) {
                        targets.add(fighter);
                        break;
                    }
                }
            }
        }
        targets.removeAll(parentTargets);
        return new ArrayList<>(targets);
    }

    /**
     * 查找同一行的其他fighter
     * @param aliveFighters
     * @param parentTargets
     * @return
     */
    private static List<Fighter> findSameRow(List<Fighter> aliveFighters, List<Fighter> parentTargets) {
        List<Fighter> targets = new ArrayList<>();
        for (Fighter parentTarget : parentTargets) {
            for (Fighter fighter : aliveFighters) {
                if (parentTarget.getPid() == fighter.getPid()) {
                    continue;
                }
                boolean check = GDPos.relativeCheck(RelativeType.SAME_ROW, parentTarget.getPos(), fighter.getPos());
                if (check) {
                    targets.add(fighter);
                }
            }
        }
        return targets;
    }

    /**
     * 查找同一列的其他figher
     * @param aliveFighters
     * @param parentTargets
     * @return
     */
    private static List<Fighter> findSameCol(List<Fighter> aliveFighters, List<Fighter> parentTargets) {
        List<Fighter> targets = new ArrayList<>();
        for (Fighter parentTarget : parentTargets) {
            for (Fighter fighter : aliveFighters) {
                if (parentTarget.getPid() == fighter.getPid()) {
                    continue;
                }
                boolean check = GDPos.relativeCheck(RelativeType.SAME_COL, parentTarget.getPos(), fighter.getPos());
                if (check) {
                    targets.add(fighter);
                }
            }
        }
        return targets;
    }

    /**
     * 查找主目标以外的单位
     * @param aliveFighters
     * @param parentTargets
     * @return
     */
    private static List<Fighter> findOther(List<Fighter> aliveFighters, List<Fighter> parentTargets) {
        List<Fighter> targets = new ArrayList<>();
        for (Fighter aliveFighter : aliveFighters) {
            if (!containFighter(parentTargets, aliveFighter)) {
                targets.add(aliveFighter);
            }
        }
        return targets;
    }

    private static boolean containFighter(List<Fighter> list, Fighter target) {
        for (Fighter fighter : list) {
            if (fighter.getPid() == target.getPid()) {
                return true;
            }
        }
        return false;
    }

}
